/**
 * 用于router动态路由的类
 * 使用:DynamicRouter.getInstance().init(router, defaultRoutes);
 * 1.router
 * 2.默认的路由
 * */
import {Router, RouteRecordNormalized, RouteRecordRaw} from 'vue-router';

export interface RouteMeta {
    title: string;
    icon?: string;
    hidden?: boolean; // 隐藏菜单,true为隐藏
}

export interface BreadcrumbItem {
    path: string;
    title: string;
    name: string;
}

// 路由的一项
export interface RouteItem {
    id?: string; //唯一id
    path: string; //    path
    name: string; // name
    component?: () => any; // 组件函数
    componentPath?: string; // 组件路径
    redirect?: string; // 父组件打开时默认跳转的路径
    fatherName?: string; // 菜单需要挂在到那个菜单下边,父元素才有
    meta?: RouteMeta;
    children?: RouteItem[];
    breadcrumb?: BreadcrumbItem[]; // 面包屑
}

interface MenuMeta {
    icon: string;
    title: string;
}

// 菜单的一项
export interface MenuItem {
    breadcrumb: BreadcrumbItem[];
    path: string;
    children: MenuItem[];
    name: string;
    meta: MenuMeta;
}

class DynamicRouter {
    static instance: any;
    private router: Router | null = null;
    private defaultRoutes: RouteRecordRaw[] = [];
    private menuReflect: { [key: string]: MenuItem } = {};

    private constructor() {
    }

    public static getInstance() {
        if (!DynamicRouter.instance) {
            DynamicRouter.instance = new DynamicRouter();
        }
        return DynamicRouter.instance;
    }

    // 初始化,一个是router本地,一个是默认的路由
    public init(router: Router, defaultRoutes: Array<RouteRecordRaw>) {
        this.router = router;
        this.defaultRoutes = defaultRoutes;
    }

    // 设置路由,传入路由值,返回menu
    public generateRoutes(routes: RouteItem[]) {
        const {router} = this;
        if (!router) {
            return [];
        }
        this.resetRouter();
        const originRoutes = this.transformRoutes(routes); // 获取原始的路径
        const showRoutes = this.filterShowRoutes(originRoutes); // 获取左侧显示的路径
        originRoutes.forEach((routeItem: any) => {
            this.setBreadcrumb(routeItem);
            if (routeItem.fatherName) {
                router.addRoute(routeItem.fatherName, routeItem);
            } else {
                router.addRoute(routeItem);
            }
        });
        return showRoutes.map((item: any) => this.genMenuItem(item));
    }

    // 通过name获取menuItem
    public getMenuByName(name: string) {
        return this.menuReflect[name];
    }

    // 处理路由数据
    private transformRoutes(routes: RouteItem[]) {
        return routes.map((route: RouteItem) => {
            const {component, componentPath} = route;
            if (!component && componentPath) {
                route.component = this._import(componentPath);
            }
            if (route.children && route.children.length) {
                route.children = this.transformRoutes(route.children);
            }
            return route;
        });
    }

    // 筛选出隐藏的路由
    private filterShowRoutes(routes: RouteItem[]) {
        return routes
            .filter((item: any) => !item.meta.hidden)
            .map((item: any) => {
                if (item.children && item.children.length) {
                    item.children = this.filterShowRoutes(item.children);
                }
                return item;
            });
    }

    // 根据参数设置route的值
    private setBreadcrumb(routeItem: any, parentBreadcrumb: any[] = []) {
        const {path, meta, children, name} = routeItem;
        const {title} = meta;
        const breadcrumb = [...parentBreadcrumb, {path, title, name}];
        routeItem.breadcrumb = breadcrumb;
        if (children) {
            children.forEach((item: any) => this.setBreadcrumb(item, breadcrumb));
        }
    }

    // 筛选右侧菜单展示需要的数据
    private genMenuItem(routeItem: any, parentPath: string = '/') {
        const {path, meta, children, name, breadcrumb} = routeItem;
        const menuItem: MenuItem = {path, name, meta, children: [], breadcrumb};
        if (!path.startsWith('/')) {
            if (parentPath.endsWith('/')) {
                menuItem.path = parentPath + path;
            } else {
                menuItem.path = `${parentPath}/${path}`;
            }
        }
        if (children && children.length > 0) {
            menuItem.children = children.map((item: any) => this.genMenuItem(item, menuItem.path));
        }
        this.menuReflect[name] = menuItem;
        return menuItem;
    }

    private _import(path: string) {
        return () => import('../' + path + '.vue');
    }

    // 用于清除路由的方法,注:vue2和vue3清除路由的方式不相同
    private resetRouter() {
        const {router, defaultRoutes} = this;
        if (!router) {
            return false;
        }
        router.getRoutes().forEach((route: RouteRecordNormalized) => {
            const {name} = route;
            if (name) {
                router.hasRoute(name) && router.removeRoute(name);
            }
        });
        defaultRoutes.forEach((item: RouteRecordRaw) => {
            router.addRoute(item);
        });
        return true;
    }
}

// 设置动态路由
export function setDynamicRoute(
    router: Router,
    defaultRoutes: RouteRecordRaw[],
    staticRoute: RouteItem[],
) {
    const instance = DynamicRouter.getInstance();
    instance.init(router, defaultRoutes);
    return instance.generateRoutes(staticRoute);
}

// 通过name值取到对应的menuList的一项
export function getMenuByName(name: string) {
    return DynamicRouter.getInstance().getMenuByName(name);
}
